// 12.1 动态内存与智能指针
/**
 * 在C++中，动态内存的管理是通过一对运算符来完成的：
 * 1. new，在动态内存中为对象分配空间并返回一个指向该对象的指针，我们可以选择对对象进行初始化；
 * 2. delete，接受一个动态对象的指针，销毁该对象，并释放与之关联的内存。
 * 新标准库提供的这两种智能指针的区别在于管理底层指针的方式：
 * 1. shared_ptr允许多个指针指向同一个对象；
 * 2. unique_ptr则“独占”所指向的对象。
 * 标准库还定义了一个名为weak_ptr的伴随类，它是一种弱引用，指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;

// 从函数返回一个unique_ptr
unique_ptr<int> clone(int p)
{
    return unique_ptr<int>(new int(p)); // 正确，从int*创建一个unique_ptr<int>
}

// 从函数返回一个unique_ptr
unique_ptr<int> clone1(int p)
{
    auto ret = unique_ptr<int>(new int(p));
    return  ret; // 正确，返回一个局部对象的拷贝
}

int main()
{
    // 12.1.5 unique_ptr
    // 一个unique_ptr“拥有”它所指向的对象。与shared_ptr不同，某个时刻只能有一个unique_ptr指向一个给定对象。
    // 当unique_ptr被销毁时，它所指向的对象也被销毁。
    // 当我们定义一个unique_ptr时，需要将其绑定到一个new返回的指针上。类似shared_ptr，初始化unique_ptr必须采用直接初始化形式：[插图]
    unique_ptr<double> p1;           // 可以指向一个double的unique_ptr
    unique_ptr<int> p2(new int(18)); // p2指向一个值为18的int
    // 由于一个unique_ptr拥有它指向的对象，因此unique_ptr不支持普通的拷贝或赋值操作：[插图][插图]
    // unique_ptr<int> p3(p2); // 错误，unique_ptr不支持拷贝操作
    unique_ptr<int> p4;
    // p4 = p2; // 错误，unique_ptr不支持赋值
    // 虽然我们不能拷贝或赋值unique_ptr，但可以通过调用release或reset将指针的所有权从一个（非const）unique_ptr转移给另一个unique：[插图]
    unique_ptr<int> p5(p2.release()); // 将所有权从p1转移至p5，release将p2置为空
    unique_ptr<int> p6(new int(1024));
    p5.reset(p6.release()); // release释放了p5原来指向的内存
    // 调用release会切断unique_ptr和它原来管理的对象间的联系。release返回的指针通常被用来初始化另一个智能指针或给另一个智能指针赋值。
    // 但是，如果我们不用另一个智能指针来保存release返回的指针，我们的程序就要负责资源的释放：[插图]
    p2.release(); // 错误，p2不会释放内存，而且我们丢失了指针
    auto p = p5.release(); // 正确，但我们必须记得delete(p)

    // 传递unique_ptr参数和返回unique_ptr
    // 不能拷贝unique_ptr的规则有一个例外：我们可以拷贝或赋值一个将要被销毁的unique_ptr。
    // 最常见的例子是从函数返回一个unique_ptr：[插图]
    unique_ptr<int> p7 = clone(7); // 从函数返回一个unique_ptr

    // 向后兼容：auto_ptr
    // 标准库的较早版本包含了一个名为auto_ptr的类，它具有unique_ptr的部分特性，但不是全部。
    // 特别是，我们不能在容器中保存auto_ptr，也不能从函数中返回auto_ptr。
    // 虽然auto_ptr仍是标准库的一部分，但编写程序时应该使用unique_ptr。

    // 向unique_ptr传递删除器
    // 类似shared_ptr，unique_ptr默认情况下用delete释放它指向的对象。
    // 与shared_ptr一样，我们可以重载一个unique_ptr中默认的删除器（参见12.1.4节，第415页）。
    // 与重载关联容器的比较操作（参见11.2.2节，第378页）类似，我们必须在尖括号中unique_ptr指向类型之后提供删除器类型。
    // unique_ptr<objT, delT> p(new objT, fcn); // p指向objT类型的对象，并使用delT类型的对象释放objT，它会调用一个名为fcn的detT类型对象
    



    return 0;
}